package io.bdmc.core.msp2000;

import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import io.bdmc.common.enums.RouteTypeEnum;
import io.bdmc.common.enums.SaveUpdateEnum;
import io.bdmc.common.utils.ByteUtil;
import io.bdmc.common.utils.CommonUtil;
import io.bdmc.common.utils.Constant;
import io.bdmc.common.utils.StringUtils;
import io.bdmc.core.msp2000.model.FanPlate;
import io.bdmc.core.msp2000.utils.FmsDeviceUtil;
import io.bdmc.core.snmp.SnmpAlarmProcess;
import io.bdmc.core.snmp.SnmpDataCache;
import io.bdmc.core.snmp.cache.ConcurrentHashMapCacheUtils;
import io.bdmc.modules.bss.entity.*;
import io.bdmc.modules.bss.model.AlarmDataType;
import io.bdmc.modules.bss.service.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

@Component
public class Msp2000AlarmProcess {
    @Autowired
    IDeviceAlarmRealtimeService _alarmService;

    @Autowired
    IDeviceAlarmService _alarmHistoryService;

    @Autowired
    IDeviceRouteService routeService;

    @Autowired
    IDeviceRouteConfigService routeConfigService;

    @Autowired
    IDeviceRouteAlarmSettingService routeAlarmSettingService;

    @Autowired
    IDeviceDeviceService deviceService;

    @Autowired
    IDeviceMeasureService measureService;

    @Autowired
    FmsDeviceUtil fmsDeviceUtil;

    @Autowired
    IDeviceMachineOsService osService;

    @Autowired
    IDeviceMachineOsModuleService osModuleService;

    @Autowired
    IDeviceFibreMatchService deviceFibreMatchService;

    @Autowired
    IDeviceAlarmMaskService _alarmMaskService;

    @Autowired
    IDeviceAlarmRealtimeService alarmRealtimeService;

    @Autowired
    IDeviceMachineDtsConfigService dtsConfigService;

    @Autowired
    SnmpAlarmProcess snmpAlarmProcess;

    @Autowired
    IFibreFibreService fibreService;

    protected static Logger logger = LoggerFactory.getLogger(Msp2000AlarmProcess.class);

    /**
     * 解析风扇告警数据
     *
     * @param fan      风扇数据
     * @param dbDevice 设备信息
     */
    @Async
    public void fanAlarm(FanPlate fan, DeviceDevice dbDevice) {
        int fanRunTime = fan.getFans().stream().mapToInt(DeviceMachineFan::getRunTime).max().getAsInt();
        int fanLifeTime = fan.getFans().stream().mapToInt(DeviceMachineFan::getFanLife).max().getAsInt();
        if (fanRunTime > fanLifeTime) {
            saveOrUpdateAlarm("LIFE", dbDevice);
        }
        if (fan.getTemperatureAlarm() == 1) {
            saveOrUpdateAlarm("TEMP", dbDevice);
        }
    }

    /**
     * 解析FSP盘告警数据
     *
     * @param fsp      FSP
     * @param dbDevice 设备信息
     */
    @Async
    public void fspAlarm(DeviceMachineFsp fsp, DeviceDevice dbDevice) {
        String alarmBinary = ByteUtil.hexString2binaryString(fsp.getAlarmInfo());
        if (alarmBinary.charAt(7) == '1') {
            saveOrUpdateAlarm("FSP_TX_ALARM", dbDevice);
        }
        if (alarmBinary.charAt(6) == '1') {
            saveOrUpdateAlarm("FSP_T1_ALARM", dbDevice);
        }
        if (alarmBinary.charAt(5) == '1') {
            saveOrUpdateAlarm("FSP_T2_ALARM", dbDevice);
        }
        if (alarmBinary.charAt(4) == '1') {
            saveOrUpdateAlarm("FSP_RX_ALARM", dbDevice);
        }
        if (alarmBinary.charAt(3) == '1') {
            SaveUpdateEnum addOrUpdate = saveOrUpdateAlarm("FSP_R1_ALARM", dbDevice);
            // 驱动告警并启动测试
            final DeviceFibreMatch deviceFibreMatch = deviceFibreMatchService
                    .selectByDeviceSnAndChannel(dbDevice.getDeviceSn(), 1);
            if (deviceFibreMatch != null && addOrUpdate == SaveUpdateEnum.ADD) {
                Msp2000DataCache.offerFspMeasureQueue(deviceFibreMatch);
            }
        }
        if (alarmBinary.charAt(2) == '1') {
            SaveUpdateEnum addOrUpdate = saveOrUpdateAlarm("FSP_R2_ALARM", dbDevice);
            final DeviceFibreMatch deviceFibreMatch = deviceFibreMatchService
                    .selectByDeviceSnAndChannel(dbDevice.getDeviceSn(), 2);
            if (deviceFibreMatch != null && addOrUpdate == SaveUpdateEnum.ADD)
                Msp2000DataCache.offerFspMeasureQueue(deviceFibreMatch);
        }
        // 模式不匹配标志
        if (fsp.getPatternMismatch().equals("01")) {
            saveOrUpdateAlarm("MODE_UNMATCH", dbDevice);
        }
        // 工作于备线路
        if (fsp.getWorkMode().equals("80") && fsp.getWorkingChannel().equals("F3")) {
            saveOrUpdateAlarm("WORKING_BACKUP", dbDevice);
        }
        // 工作模式事件
        if (fsp.getWorkMode().equals("50") || fsp.getWorkMode().equals("60")) {
            saveOrUpdateAlarm("WORKING_NOT_AUTO", dbDevice);
        }
        // fsp收发模块误码
        if (fsp.getStateAlarm().contains("4,")) {
            saveOrUpdateAlarm("FSP_RS_ERROR", dbDevice);
        }
        // fsp收不到数据-没有应答
        if (fsp.getStateAlarm().contains("5,")) {
            saveOrUpdateAlarm("FSP_NO_ANSWER", dbDevice);
        }
        // fsp线路衰耗告警-RXD收纤
        if (fsp.getStateAlarm().contains("6,")) {
            saveOrUpdateAlarm("FSP_LINE_LOSS", dbDevice);
        }
        // fsp光开关驱动电压告警-小于5.5V
        if (fsp.getStateAlarm().contains("7,")) {
            saveOrUpdateAlarm("FSP_SWITCH_POWER", dbDevice);
        }
    }

    /**
     * 解析EDFA盘告警数据
     *
     * @param edfa     EDFA
     * @param dbDevice 设备信息
     */
    @Async
    public void edfaAlarm(DeviceMachineEdfa edfa, DeviceDevice dbDevice) {
        parseAlarmStateAndSave(edfa.getAlarmState1(), edfa.getAlarmState2(), dbDevice);
    }

    /**
     * 解析RFA盘告警数据
     *
     * @param rfa      rfa数据
     * @param dbDevice 设备信息
     */
    @Async
    public void rfaAlarm(DeviceMachineRfa rfa, DeviceDevice dbDevice) {
        parseAlarmStateAndSave(rfa.getAlarmState1(), "0000000000000000", dbDevice);
    }

    /**
     * 解析RT盘告警数据
     *
     * @param rt       RT数据
     * @param dbDevice 设备信息
     */
    @Async
    public void rtAlarm(DeviceMachineRt rt, DeviceDevice dbDevice) {
        parseAlarmStateAndSave(rt.getAlarmState1(), rt.getAlarmState2(), dbDevice);

        if (rt.getDdm()!=null && rt.getDdm().length() == 4) {
            saveAlarmByDDM(rt.getDdm(), dbDevice);
            // 10G
        } else if (rt.getDdm()!=null && rt.getDdm().length() == 10) {
            List<String> lt = new ArrayList<>();
            String alarmBinary1 = ByteUtil.hexString2binaryString(rt.getDdm().substring(0, 2));
            String alarmBinary2 = ByteUtil.hexString2binaryString(rt.getDdm().substring(2, 4));
            String alarmBinary3 = ByteUtil.hexString2binaryString(rt.getDdm().substring(4, 6));
            String alarmBinary4 = ByteUtil.hexString2binaryString(rt.getDdm().substring(6, 8));
            String alarmBinary5 = ByteUtil.hexString2binaryString(rt.getDdm().substring(8, 10));

            lt.add(alarmBinary1);
            lt.add(alarmBinary2);
            lt.add(alarmBinary3);
            lt.add(alarmBinary4);
            lt.add(alarmBinary5);

            String[] alarmCode = new String[] { "TEMP_HIGH", "TEMP_LOW", "", "", "TX_BIAS_CURRENT_HIGH",
                    "TX_BIAS_CURRENT_LOW", "SEND_POWER_HIGH", "SEND_POWER_LOW", "REC_POWER_HIGH", "REC_POWER_LOW",
                    "L_AUX1HIGHT_ALARM", "L_AUX1LOW_ALARM", "L_AUX2High_ALARM", "L_AUX2LOW_ALARM", "", "", "L_TX_NR",
                    "L_TX_FAULT", "L_TX_CDR_NOT_LOCKED", "L_RX_NR", "L_RX_LOS", "L_RX_CDR_NOT_LOCKED", "L_MOD_NR",
                    "L_RESET_COMPLETE", "L_VCC5_HIGHT_ALARM", "L_VCC5_LOW_ALARM", "L_VCC3_HIGHT_ALARM",
                    "L_VCC3_LOW_ALARM", "L_VCC2_HIGHT_ALARM", "L_VCC2_LOW_ALARM", "L_VEE5_HIGHT_ALARM",
                    "L_VEE5_LOW_ALARM", "l_APD_SUPPLY_FAULT", "L_TEC_FAULT", "L_WAVELENGTH_UNLOCKED", "L_BAD_CHANNEL",
                    "L_NEW_CHANNEL", "L_UR_SUPPORTED_TX_DITHER", "", "" };

            parseAlarmCodeAndSave(alarmCode, lt, dbDevice, false);
        }
    }

    /**
     * 解析fms 设备绑定的路由是否有告警信息
     *
     * @param measure 光缆测试信息
     */
    @Async
    public void parseFmsAlarm(DeviceMeasure measure, String fmsDeviceSn) {
        // 获取路由通道的标准值
        DeviceMeasure standardMeasure = measureService.getStandardByRouteAndChannel(measure.getRouteId(),
                measure.getChannel());
        String alarmFmsCacheKey = Constant.MSP2000FMSAlarmProcessPrefix + fmsDeviceSn;
        if (standardMeasure == null) {
            logger.error("路由ID" + measure.getRouteId() + "未设置标准值");
            ConcurrentHashMapCacheUtils.deleteCache(alarmFmsCacheKey);
            return;
        }
        DeviceDevice device = deviceService.getDeviceBySn(measure.getDeviceSn());
        // 查询该设备是否存在路由
        DeviceRoute route = routeService.getById(measure.getRouteId());
        if (route == null) {
            logger.error("route=null");
            ConcurrentHashMapCacheUtils.deleteCache(alarmFmsCacheKey);
            return;
        }
        // 获取高级设置，判断告警
        List<DeviceRouteConfig> routeConfigList = routeConfigService
                .list(new QueryWrapper<DeviceRouteConfig>().eq("route_id", route.getRouteId()));
        DeviceRouteConfig config;
        Optional<DeviceRouteConfig> first = routeConfigList.stream()
                .filter(r -> r.getChannel().equals(measure.getChannel())).findFirst();
        if (first.isPresent()) {
            config = first.get();
        } else {
            // 没有查到的逻辑
            logger.error(measure.getDeviceSn() + "设备通道未设置告警：" + measure.getChannel());
            ConcurrentHashMapCacheUtils.deleteCache(alarmFmsCacheKey);
            return;
        }
        // 获取指定测量通道
        BigDecimal lengthDiff = measure.getLength().subtract(standardMeasure.getLength()).abs();
        BigDecimal padControlDiff = measure.getPadControl().subtract(standardMeasure.getPadControl());
        if (lengthDiff.compareTo(config.getLengthAlarm()) < 0) {
            // 长度正常 才会判断衰耗告警
            // 差值在正常范围内，判断有没有衰耗,解锁fms告警锁，清除告警次数统计
            ConcurrentHashMapCacheUtils.deleteCache(alarmFmsCacheKey);
            ConcurrentHashMapCacheUtils.deleteCache(Constant.routeOTDRAlarmTimes + fmsDeviceSn);
            removeRealtimeAndInsertHistory(device.getDeviceSn(), measure.getChannel(), Constant.ALARM_Distance);
            if (padControlDiff.compareTo(config.getPadControlUrg()) > 0) {
                saveOrUpdateAlarm(Constant.ALARM_Loss, device, measure.getChannel(), "A");
            } else if (padControlDiff.compareTo(config.getPadControlMain()) > 0) {
                saveOrUpdateAlarm(Constant.ALARM_Loss, device, measure.getChannel(), "B");
            } else if (padControlDiff.compareTo(config.getPadControlSec()) > 0) {
                saveOrUpdateAlarm(Constant.ALARM_Loss, device, measure.getChannel(), "C");
            } else if (padControlDiff.compareTo(config.getPadControlProm()) > 0) {
                saveOrUpdateAlarm(Constant.ALARM_Loss, device, measure.getChannel(), "D");
            } else {// 正常范围内，判断之前有无告警，有告警就设置正常
                removeRealtimeAndInsertHistory(device.getDeviceSn(), measure.getChannel(), Constant.ALARM_Loss);
            }
            return;
        }
        // 下面是有长度告警的逻辑，lengthDiff.compareTo(config.getLengthAlarm()) >= 0)
        // 缓存中记录fms告警测量的次数
        RouteTypeEnum routeTypeEnum = RouteTypeEnum.valueOf(route.getRouteType());
        switch (routeTypeEnum) {
            case realTimeRoute: {
                // 实时路由 需要对比次数
                int alarmCount = setAlarmTimesCacheAndAddOne(route.getFmsSn());
                if (alarmCount < config.getCompareTimes()) {
                    // 次数不到就记录次数，释放fms锁等下次周期测试判断
                    ConcurrentHashMapCacheUtils.deleteCache(alarmFmsCacheKey);
                    return;
                }
                // 次数到了就看是否需要周期测试,需要就进行周期测试，否则就产生告警信息并推送，释放fms告警锁
                dealRealAlarm(measure, standardMeasure.getLength().toString(),
                        needActualMeasure(measure, alarmCount, config.getCompareTimes()), device, route,
                        alarmFmsCacheKey);
                break;
            }
            case pollingRoute: {
                // 循环监测路由 需要 多次启动测量 包括普通和精准
                int alarmCount = setAlarmTimesCacheAndAddOne(route.getFmsSn());
                if (alarmCount < config.getCompareTimes()) {
                    startNormalMeasure(route, measure);
                    // 启动普通测量，并跳出告警判断，但不释放fms告警锁
                    return;
                }
                // 次数到了就看是否需要精准测试,需要就进行精准测试，否则就产生告警信息并推送，释放fms告警锁
                dealRealAlarm(measure, standardMeasure.getLength().toString(),
                        needActualMeasure(measure, alarmCount, config.getCompareTimes()), device, route,
                        alarmFmsCacheKey);
                break;
            }
            case offlineRoute:
            case fspRoute:
                // fsp路由和离线不需要判断次数，直接看是否需要精准测试,需要就进行精准测试，否则就产生告警信息并推送，释放fms告警锁
                dealRealAlarm(measure, standardMeasure.getLength().toString(), needActualMeasure(measure), device,
                        route, alarmFmsCacheKey);
                break;
        }
    }

    @Async
    public void parsePowerAlarm(List<DeviceMachinePower> powerList) {
        for (DeviceMachinePower power : powerList) {
            if (power.getPowerType() != null && !power.getPowerType().equals("不存在")) {
                logger.info(power.getDiskSn() + ":" + power.getPowerLocation() + ":" + power.getPowerType() + ":"
                        + power.getPowerVol());
                BigDecimal vol = new BigDecimal(power.getPowerVol());
                String alarmType = "";
                if (vol.compareTo(new BigDecimal("11")) < 0) {
                    // 电压告警
                    alarmType = "POWER_VOL_LOW";
                } else if (vol.compareTo(new BigDecimal("13")) > 0) {
                    alarmType = "POWER_VOL_HIGH";
                }
                if (StringUtils.isNotEmpty(alarmType)) {
                    DeviceDevice d = deviceService.getDeviceBySn(power.getDiskSn());
                    saveOrUpdateAlarm(alarmType, d, 0, "", power.getPowerLocation() + "电压异常告警" + power.getPowerVol(),
                            false);
                }
            }
        }
    }

    /**
     * 根据测量信息判断是否需要精准测量
     * 
     * @param measure      测量信息
     * @param alarmCount   实际告警次数
     * @param compareTimes 配置需要对比的次数
     * @return 是否需要精准测量
     */
    boolean needActualMeasure(DeviceMeasure measure, int alarmCount, int compareTimes) {
        return alarmCount == compareTimes && needActualMeasure(measure);
    }

    boolean needActualMeasure(DeviceMeasure measure) {
        return measure.getEndPosition().compareTo(5) > 0 && measure.getLength().compareTo(new BigDecimal(5)) < 0;
    }

    /**
     * 对缓存中告警次数key加1，不存在就创建key
     *
     * @param sn fms-sn
     */
    int setAlarmTimesCacheAndAddOne(String sn) {
        String timesKey = Constant.routeOTDRAlarmTimes + sn;
        if (ConcurrentHashMapCacheUtils.isExist(timesKey)) {
            Integer alarmCount = (Integer) ConcurrentHashMapCacheUtils.getCache(timesKey);
            if (alarmCount == null)
                alarmCount = 0;
            alarmCount++;
            ConcurrentHashMapCacheUtils.setCache(timesKey, alarmCount);
            return alarmCount;
        } else {
            ConcurrentHashMapCacheUtils.setCache(timesKey, 1);
            return 1;
        }
    }

    // 真正处理告警但方法
    void dealRealAlarm(DeviceMeasure measure, String standLength, boolean needActualMeasure, DeviceDevice device,
            DeviceRoute route, String alarmFmsCacheKey) {
        if (needActualMeasure) {
            startActualMeasure(route, measure);
        } else {
            // 不需要精准测量就释放fms锁，让周期测试测量下一个通道
            ConcurrentHashMapCacheUtils.deleteCache(alarmFmsCacheKey);
            String location = snmpAlarmProcess.calcLocation(measure.getDeviceSn(),
                    measure.getChannel(), measure.getLength());

            String alarmMsg = standLength + "|" + measure.getLength() + "|" + location;
            logger.info(alarmMsg);
            saveOrUpdateAlarm(Constant.ALARM_Distance, device, measure.getChannel(), "A", alarmMsg, true);
        }
    }

    /**
     * 启动精准测量
     *
     * @param route   路由信息
     * @param measure 测量信息
     */
    void startActualMeasure(DeviceRoute route, DeviceMeasure measure) {
        logger.info("测量结果异常，正在启动精准测试论证测量结果.routeId" + route.getRouteId() + ",measureId:" + measure.getMeasureId());
        DeviceDevice fms = deviceService.getDeviceBySn(route.getFmsSn());
        DeviceDevice parent = deviceService.getById(fms.getParentId());
        String ip = fms.isOTDR() ? fms.getIpaddr() : parent.getIpaddr();
        int port = fms.isOTDR() ? fms.getDevicePort() : parent.getDevicePort();
        boolean setFmsResult = fmsDeviceUtil.setFmsConfig(fms.getLayerRow(), ip, port, "5", "10", "0.32",
                fms.getDeviceType(), fms.getDeviceSn());
        if (setFmsResult) {
            // 启动FMS设备测试
            LocalDateTime now = LocalDateTime.now();
            boolean startResult = fmsDeviceUtil.startMeasure(fms.getLayerRow(), ip, port, fms.getDeviceType());
            if (!startResult) {
                startResult = fmsDeviceUtil.startMeasure(fms.getLayerRow(), ip, port, fms.getDeviceType());
            }
            logger.info(
                    "精准测试启动结果：" + startResult + ",fms-sn:" + fms.getDeviceSn() + ",channel:" + measure.getChannel());
            if (startResult) {
                DeviceMeasure accurateMeasure = new DeviceMeasure().setAverageNum(1).setDeviceId(measure.getDeviceId())
                        .setDeviceIp(parent.getIpaddr()).setDeptId(measure.getDeptId()).setAction(5)
                        .setDeviceSn(measure.getDeviceSn()).setEndPosition(5).setPulseWidth(10).setMeasureStatus(2)
                        .setStartTime(now).setResolution(new BigDecimal("0.32")).setCreateBy("system")
                        .setCreateTime(now).setChannel(measure.getChannel()).setRouteId(route.getRouteId());
                accurateMeasure.setAutoTaskNum(CommonUtil.getOrderIdByUUId());
                accurateMeasure.insert();
                String manualKey = Constant.MSP2000MeasureNowMapPrefix + fms.getDeviceSn();
                ConcurrentHashMapCacheUtils.setCache(manualKey, accurateMeasure);
            }
        }
    }

    void startNormalMeasure(DeviceRoute route, DeviceMeasure measure) {
        try {
            logger.info("测量结果异常，正在启动普通测试论证测量，routeId：" + route.getRouteId() + ",measureId:" + measure.getMeasureId());
            DeviceDevice fms = deviceService.getDeviceBySn(route.getFmsSn());
            DeviceDevice parent = deviceService.getById(fms.getParentId());
            String ip = fms.isOTDR() ? fms.getIpaddr() : parent.getIpaddr();
            int port = fms.isOTDR() ? fms.getDevicePort() : parent.getDevicePort();
            // 启动FMS设备测试
            LocalDateTime now = LocalDateTime.now();
            boolean startResult = fmsDeviceUtil.startMeasure(fms.getLayerRow(), ip, port, fms.getDeviceType());
            if (!startResult) {
                startResult = fmsDeviceUtil.startMeasure(fms.getLayerRow(), ip, port, fms.getDeviceType());
            }
            logger.info(
                    "普通测试启动结果：" + startResult + ",fms-sn:" + fms.getDeviceSn() + ",channel:" + measure.getChannel());
            if (startResult) {
                String manualKey = Constant.MSP2000MeasureNowMapPrefix + fms.getDeviceSn();
                DeviceMeasure accurateMeasure = new DeviceMeasure();
                ConcurrentHashMapCacheUtils.setCache(manualKey, accurateMeasure);
                accurateMeasure.setAverageNum(1).setDeviceId(measure.getDeviceId()).setDeviceIp(parent.getIpaddr())
                        .setDeptId(measure.getDeptId()).setAction(5).setDeviceSn(measure.getDeviceSn())
                        .setEndPosition(measure.getEndPosition()).setPulseWidth(measure.getPulseWidth())
                        .setMeasureStatus(2)
                        .setStartTime(now).setResolution(measure.getResolution()).setCreateBy("system")
                        .setCreateTime(now)
                        .setChannel(measure.getChannel()).setRouteId(route.getRouteId());
                accurateMeasure.setAutoTaskNum(CommonUtil.getOrderIdByUUId());
                accurateMeasure.insert();
                ConcurrentHashMapCacheUtils.setCache(manualKey, accurateMeasure);
            }
        } catch (Exception ex) {
            logger.info("普通测试论证测量结果----routeId：" + route.getRouteId() + "出现异常");
            logger.error(ex.toString());
        }
    }

    // 将实时告警取消，然后插入历史告警
    private Integer removeRealtimeAndInsertHistory(String deviceSn, Integer channel, String alarmTypeCode) {
        List<DeviceAlarmRealtime> alarmRealtimeList = _alarmService.list(new QueryWrapper<DeviceAlarmRealtime>()
                .eq("device_sn", deviceSn).eq("channel", channel).eq("alarm_type_code", alarmTypeCode));
        int count = 0;
        for (DeviceAlarmRealtime alarmRealtime : alarmRealtimeList) {
            // LocalDateTime now = LocalDateTime.now();
            _alarmHistoryService.saveByRealtimeAlarm(alarmRealtime);
            snmpAlarmProcess.sendFibreNormalEmail(deviceSn, channel);
            alarmRealtime.deleteById();
            // 有紧急告警才算次数 opm 离线路由使用这个值是否要驱动otdr测试
            if (alarmRealtime.getAlarmLevel().equals("A")) {
                count++;
            }
        }
        return count;
    }

    /**
     * 解析FEC盘告警数据
     *
     * @param fec      FEC数据
     * @param dbDevice 设备信息
     */
    @Async
    public void fecAlarm(DeviceMachineFec fec, DeviceDevice dbDevice) {
        if (fec == null)
            return;
        // DDM报警
        if (fec.getFecModuleList() != null && fec.getFecModuleList().size() > 0) {
            for (DeviceMachineFecModule module : fec.getFecModuleList()) {
                // 2.5G
                if (module.getDdm().length() == 4) {
                    saveAlarmByDDM(module.getDdm(), dbDevice);
                    // 10G
                }
                // else if (module.getDdm().length() == 10) {
                //
                // }
            }
        }

        // 模块报警
        List<String> lt = new ArrayList<>();
        String alarmBinary1 = ByteUtil.hexString2binaryString(fec.getAlarm().substring(0, 2));
        String alarmBinary2 = ByteUtil.hexString2binaryString(fec.getAlarm().substring(2, 4));
        String alarmBinary3 = ByteUtil.hexString2binaryString(fec.getAlarm().substring(4, 6));
        String alarmBinary4 = ByteUtil.hexString2binaryString(fec.getAlarm().substring(6, 8));

        lt.add(alarmBinary1);
        lt.add(alarmBinary2);
        lt.add(alarmBinary3);
        lt.add(alarmBinary4);

        String[] alarmCode = new String[] { "Cl_RX_OOF", "Cl_RX_LOF", "Cl_RX_LOS", "Cl_RX_MLOS", "Cl_RX_LOL",
                "Cl_TX_LOS", "Cl_RX_MLOL", "Cl_TX_MLOS", "LI_RX_OOF", "LI_RX_LOF", "LI_RX_LOS", "LI_RX_MLOS",
                "LI_RX_LOL", "LI_TX_LOS", "LI_RX_MLOL", "LI_TX_MLOS", "CL_RX_OTU_AIS", "CL_RX_MS_RDI", "LI_RX_LOS",
                "LI_RX_MLOS", "LI_RX_LOL", "LI_TX_LOS", "LI_RX_MLOL", "LI_TX_MLOS", "RX_OTU_AIS", "", "", "", "", "",
                "", "" };

        parseAlarmCodeAndSave(alarmCode, lt, dbDevice, true);
        // for (int i = 0; i < lt.size(); i++) {
        // if (lt.get(i).contains("1")) {
        // char[] chars = lt.get(i).toCharArray();
        //
        // for (int j = 0; j < chars.length; j++) {
        // if (chars[7-j] == '1') {
        // saveOrUpdateAlarm(alarmCode[i * 8 + j], dbDevice);
        // }
        // }
        // }
        // }
    }

    /**
     * 判断opm设备有没有离线监测的路由，并进行告警
     *
     * @param opm opm
     */
    @Async
    public void parseOpmData(DeviceMachineOpm opm) {
        final DeviceRoute route = routeService
                .getOne(new QueryWrapper<DeviceRoute>().eq("opm_sn", opm.getDiskSn()).last("limit 1"));
        if (route == null || StringUtils.isEmpty(route.getBindInfo()))
            return;
        JSONArray bindInfoList = JSONArray.parseArray(route.getBindInfo());
        for (Object obj : bindInfoList) {
            JSONObject bindInfo = (JSONObject) obj;
            Integer opmModuleNum = bindInfo.getInteger("opmModuleNum");
            Integer oswChannel = bindInfo.getInteger("oswChannel");
            DeviceRouteAlarmSetting alarmSetting = routeAlarmSettingService
                    .getOne(new QueryWrapper<DeviceRouteAlarmSetting>().eq("route_id", route.getRouteId())
                            .eq("opm_sn", opm.getDiskSn()).eq("channel", opmModuleNum).last("limit 1"));
            if (alarmSetting == null)// 通道未设置告警
                continue;
            BigDecimal opmModuleLightPower;
            Optional<DeviceMachineOpmModule> opmModuleOptional = opm.getOpmModuleList().stream()
                    .filter(r -> r.getOpmModuleNum().equals(opmModuleNum)).findAny();
            if (opmModuleOptional.isPresent()) {
                opmModuleLightPower = new BigDecimal(opmModuleOptional.get().getLightPower());
            } else {
                continue;
            }

            final DeviceFibreMatch match = deviceFibreMatchService.selectByDeviceSnAndChannel(opm.getDiskSn(),
                    opmModuleNum);
            if (match != null) {
                DeviceMachineOs os = osService.selectLastOsData(match.getTargetDeviceSn());
                BigDecimal osLightPower;
                if (os != null) {
                    final DeviceMachineOsModule osModule = osModuleService
                            .getOne(new QueryWrapper<DeviceMachineOsModule>().eq("machine_os_id", os.getMachineOsId())
                                    .eq("os_module_num", match.getTargetDeviceChannel()).last("limit 1"));
                    if (osModule != null) {
                        osLightPower = new BigDecimal(osModule.getLightPower());
                    } else {
                        logger.info("opm绑定os，osModule为空");
                        return;
                    }
                } else {
                    if (StringUtils.isEmpty(match.getLightPower())) {
                        logger.info("opm未绑定os，且默认lightpower为空");
                        return;
                    }
                    osLightPower = new BigDecimal(match.getLightPower());
                }
                // 计算
                BigDecimal relativePower = osLightPower.subtract(opmModuleLightPower)
                        .subtract(alarmSetting.getPowerStandard());
                String alarmLevel = "";
                String alarmMsg = "";
                boolean nowAlarmLevelA = false;
                logger.info("relativePower:" + relativePower);
                alarmMsg += "光缆当前衰耗:" + osLightPower.subtract(opmModuleLightPower) + "dB,参考衰耗:"
                        + alarmSetting.getPowerStandard() + "dB";
                if (relativePower.compareTo(alarmSetting.getPowerUrg()) > 0) {
                    alarmLevel = "A";
                    nowAlarmLevelA = true;
                } else if (relativePower.compareTo(alarmSetting.getPowerMain()) > 0) {
                    alarmLevel = "B";
                } else if (relativePower.compareTo(alarmSetting.getPowerSec()) > 0) {
                    alarmLevel = "C";
                } else if (relativePower.compareTo(alarmSetting.getPowerProm()) > 0) {
                    alarmLevel = "D";
                } else {
                    // 正常情况，需要清opm告警。
                    int count = removeRealtimeAndInsertHistory(opm.getDiskSn(), alarmSetting.getChannel(),
                            "OPM_LIGHT_POWER_ALARM");
                    if (count > 0) {
                        // 启动fms测试
                        DeviceRouteConfig routeConfig = routeConfigService.selectByRouteIdAndChannel(route.getRouteId(),
                                oswChannel);
                        Msp2000DataCache.offerOpmMeasureQueue(routeConfig);
                    }
                }
                if (!StringUtils.isEmpty(alarmLevel)) {
                    boolean isLastAlarmLevelA = false;
                    DeviceAlarmRealtime alarmRealtime = _alarmService
                            .getOne(new QueryWrapper<DeviceAlarmRealtime>().eq("device_sn", opm.getDiskSn())
                                    .eq("alarm_type_code", "OPM_LIGHT_POWER_ALARM")
                                    .eq("channel", alarmSetting.getChannel()).last("limit 1"));
                    if (alarmRealtime != null) {
                        logger.info("存在alarmRealtime,alarmId:" + alarmRealtime.getAlarmId() + ",level:"
                                + alarmRealtime.getAlarmLevel());
                        // 找出之前的alarmLevel 看是不是A，如果是A就不用再测试
                        if (alarmRealtime.getAlarmLevel().equals("A"))
                            isLastAlarmLevelA = true;
                    }
                    // 产生opm告警
                    saveOrUpdateAlarm("OPM_LIGHT_POWER_ALARM", deviceService.getDeviceBySn(opm.getDiskSn()),
                            alarmSetting.getChannel(),
                            alarmLevel, alarmMsg, false);
                    // 驱动离线路由otdr测试
                    DeviceRouteConfig routeConfig = routeConfigService.selectByRouteIdAndChannel(route.getRouteId(),
                            oswChannel);
                    // 驱动离线路由otdr测试,之前没有告警获取上一次不是紧急告警
                    if (routeConfig == null) {
                        logger.info("不存在routeConfig,routeId:" + route.getRouteId());
                        continue;
                    }
                    logger.info("存在routeConfig，nowAlarmLevelA:" + nowAlarmLevelA + "," + "isLastAlarmLevelA:"
                            + nowAlarmLevelA);
                    if (nowAlarmLevelA && !isLastAlarmLevelA) {
                        logger.info("FMS驱动测试");
                        Msp2000DataCache.offerOpmMeasureQueue(routeConfig);
                    } else {
                        logger.info("不进行FMS驱动测试");
                    }
                }

            }

        }
    }

    /**
     * 解析rt，rfa，edfa报警状态并入库
     *
     * @param alarmState1 状态1
     * @param alarmState2 状态2
     * @param dbDevice    设备信息
     */
    private void parseAlarmStateAndSave(String alarmState1, String alarmState2, DeviceDevice dbDevice) {
        List<String> lt = new ArrayList<>();
        String alarmBinary1 = alarmState1.substring(0, 8);
        String alarmBinary2 = alarmState1.substring(8, 16);
        String alarmBinary3 = alarmState1.substring(16, 24);
        lt.add(alarmBinary1);
        lt.add(alarmBinary2);
        lt.add(alarmBinary3);
        if (!StringUtils.isEmpty(alarmState2)) {
            String alarmBinary4 = alarmState2.substring(0, 8);
            String alarmBinary5 = alarmState2.substring(8, 16);
            lt.add(alarmBinary4);
            lt.add(alarmBinary5);
        }
        String[] alarmCode = new String[] { "SD1_INPUT_LOW", "SD1_INPUT_HIGH", "SD1_OUPUT", "SD1_INPUT_TOO_HIGH",
                "SD2_INPUT_LOW", "SD2_INPUT_HIGH", "SD2_OUPUT", "SD2_INPUT_TOO_HIGH", "MTH", "MTL", "MTX", "",
                "SD1_LIGHT_CLOSE", "D2_LIGHT_CLOSE", "CT", "RFL", "B1_CURRENT", "B1_POWER", "B1_TEMP", "B1_COLD",
                "B2_CURRENT", "B2_POWER", "B2_TEMP", "B2_COLD", "B3_CURRENT", "B3_POWER", "B3_TEMP", "B3_COLD",
                "B4_CURRENT", "B4_POWER", "B4_TEMP", "B4_COLD", "B5_CURRENT", "B5_POWER", "B5_TEMP", "B5_COLD",
                "B6_CURRENT", "B6_POWER", "B6_TEMP", "B6_COLD" };
        parseAlarmCodeAndSave(alarmCode, lt, dbDevice, false);
    }

    /**
     * 解析报警代码并入库
     *
     * @param alarmCodeArr 报警代码数组
     * @param lt           list
     * @param dbDevice     设备信息
     */
    void parseAlarmCodeAndSave(String[] alarmCodeArr, List<String> lt, DeviceDevice dbDevice, boolean isFecModule) {
        for (int i = 0; i < lt.size(); i++) {

            if (lt.get(i).contains("1")) {
                char[] chars = lt.get(i).toCharArray();

                for (int j = 0; j < chars.length; j++) {
                    int index = isFecModule ? 7 - j : j;
                    if (chars[index] == '1') {
                        saveOrUpdateAlarm(alarmCodeArr[i * 8 + j], dbDevice);
                    }
                }
            }
        }
    }

    /**
     * 插入rt设备或fec设备的ddm告警
     *
     * @param ddm      ddm值
     * @param dbDevice 设备
     */
    void saveAlarmByDDM(String ddm, DeviceDevice dbDevice) {
        String alarmBinary = ByteUtil.hexString2binaryString(ddm.substring(0, 2));
        String alarmBinary1 = ByteUtil.hexString2binaryString(ddm.substring(2, 4));

        if (alarmBinary.charAt(0) == '1') {
            saveOrUpdateAlarm("TEMP_HIGH", dbDevice);
        }
        if (alarmBinary.charAt(1) == '1') {
            saveOrUpdateAlarm("TEMP_LOW", dbDevice);
        }
        if (alarmBinary.charAt(2) == '1') {
            saveOrUpdateAlarm("POWER_VOL_HIGH", dbDevice);
        }
        if (alarmBinary.charAt(3) == '1') {
            saveOrUpdateAlarm("POWER_VOL_LOW", dbDevice);
        }
        if (alarmBinary.charAt(4) == '1') {
            saveOrUpdateAlarm("TX_BIAS_CURRENT_HIGH", dbDevice);
        }
        if (alarmBinary.charAt(5) == '1') {
            saveOrUpdateAlarm("TX_BIAS_CURRENT_LOW", dbDevice);
        }
        if (alarmBinary.charAt(6) == '1') {
            saveOrUpdateAlarm("SEND_POWER_HIGH", dbDevice);
        }
        if (alarmBinary.charAt(7) == '1') {
            saveOrUpdateAlarm("SEND_POWER_LOW", dbDevice);
        }
        if (alarmBinary1.charAt(0) == '1') {
            saveOrUpdateAlarm("REC_POWER_HIGH", dbDevice);
        }
        if (alarmBinary1.charAt(1) == '1') {
            saveOrUpdateAlarm("REC_POWER_LOW", dbDevice);
        }
        if (alarmBinary1.charAt(2) == '1') {
            saveOrUpdateAlarm("TX_DISABLE_STATE", dbDevice);
        }
        if (alarmBinary1.charAt(3) == '1') {
            saveOrUpdateAlarm("SOFT_TX_DISABLE", dbDevice);
        }
        if (alarmBinary1.charAt(4) == '1') {
            saveOrUpdateAlarm("LOL", dbDevice);
        }
        if (alarmBinary1.charAt(5) == '1') {
            saveOrUpdateAlarm("TX_FAULT", dbDevice);
        }
        if (alarmBinary1.charAt(6) == '1') {
            saveOrUpdateAlarm("LOS", dbDevice);
        }
        if (alarmBinary1.charAt(7) == '1') {
            saveOrUpdateAlarm("DDM", dbDevice);
        }
    }

    // 新增或更新告警信息

    /**
     * @param alarmTypeCode 告警类型
     * @param dbDevice      告警的设备
     * @param channel       设备的通道
     * @param alarmLevel    人为判定的告警等级
     * @param alarmMsg      人为判定的告警信息,ch或dts温度告警需要格式化成 12|10|113.222,30.1234
     * @param sendEmailSms  是否需要发送短信
     * @return 更新或插入
     */
    SaveUpdateEnum saveOrUpdateAlarm(String alarmTypeCode, DeviceDevice dbDevice, Integer channel, String alarmLevel,
            String alarmMsg, Boolean sendEmailSms) {
        SaveUpdateEnum resultType = SaveUpdateEnum.UNKNOWN;
        try {
            logger.info("saveOrUpdateAlarm:alarmTypeCode:" + alarmTypeCode + ",channel:" + channel + "alarmLevel:"
                    + alarmLevel + ",alarmMsg" + alarmMsg);
            DeviceAlarmType deviceAlarmType = SnmpDataCache.getAlarmTypeListByCode(alarmTypeCode);
            if (deviceAlarmType == null) {
                logger.info("alarmType为空。");
                return resultType;
            }
            boolean isMask = _alarmMaskService.isAlarmMask(dbDevice.getDeviceSn(), deviceAlarmType.getAlarmTypeCode());
            if (isMask) {
                logger.info("告警被屏蔽：" + deviceAlarmType.getAlarmTypeCode() + "," + dbDevice.getDeviceSn());
                return resultType;
            }

            LocalDateTime now = LocalDateTime.now();
            DeviceAlarmRealtime alarmRealtime = _alarmService.getLastOneExistAlarm(dbDevice.getDeviceSn(),
                    deviceAlarmType.getAlarmTypeCode(), channel);
            if (alarmRealtime != null) {
                logger.info("当前设备存在告警信息，将更新时间等。");
                alarmRealtime.setLastTime(now);
                if (!StringUtils.isEmpty(alarmLevel)) {
                    alarmRealtime.setAlarmLevel(alarmLevel);
                }
                if (!StringUtils.isEmpty(alarmMsg) && !deviceAlarmType.getAlarmTypeCode().equals("TEMP_TOO_HIGH")
                        && !deviceAlarmType.getAlarmTypeCode().equals(Constant.ALARM_Distance)) {
                    alarmRealtime.setAlarmMsg(alarmMsg);
                }
                final boolean b = alarmRealtime.updateById();
                logger.info("告警更新结果：" + b);
                resultType = SaveUpdateEnum.UPDATE;
            } else {
                logger.info("当前设备不存在告警信息，新增告警。");
                if (StringUtils.isEmpty(alarmMsg)) {
                    alarmMsg = deviceAlarmType.getAlarmTypeName();
                }
                DeviceAlarmRealtime alarm = new DeviceAlarmRealtime();
                alarm.setDeptId(dbDevice.getDeptId());
                alarm.setChannel(channel);
                if (deviceAlarmType.getAlarmTypeCode().equals("TEMP_TOO_HIGH")) {
                    alarm.setDataType(AlarmDataType.DTS.name());
                } else {
                    alarm.setDataType(AlarmDataType.MSP2000.name());
                }
                alarm.setAlarmTypeId(deviceAlarmType.getAlarmTypeId());
                alarm.setAlarmTypeName(deviceAlarmType.getAlarmTypeName());
                alarm.setAlarmTypeCode(deviceAlarmType.getAlarmTypeCode());
                alarm.setAlarmLevel(StringUtils.isEmpty(alarmLevel) ? deviceAlarmType.getAlarmLevelCode() : alarmLevel);
                alarm.setProcessed(false);
                alarm.setStartTime(now);
                alarm.setLastTime(now);
                alarm.setDeviceId(dbDevice.getDeviceId());
                alarm.setDeviceSn(dbDevice.getDeviceSn());
                alarm.setDeviceName(dbDevice.getDeviceName());
                alarm.setDeviceModel(dbDevice.getDeviceType());
                alarm.setCreateTime(now);
                if (deviceAlarmType.getAlarmTypeCode().equals(Constant.ALARM_Distance)
                        || deviceAlarmType.getAlarmTypeCode().equals("TEMP_TOO_HIGH")) {
                    String[] args = alarmMsg.split("\\|");
                    String msg;
                    if (deviceAlarmType.getAlarmTypeCode().equals(Constant.ALARM_Distance)) {
                        msg = String.format("光缆长度变化前%skm,变化后%skm,经纬度位置:%s", args[0], args[1], args[2]);
                    } else
                        msg = String.format("DTS温度过高告警，光缆长度%skm,告警起点位置%skm,经纬度位置:%s", args[0], args[1], args[2]);
                    alarm.setAlarmMsg(msg);
                    alarm.setMonitorValue1(args[0]);// 原始长度
                    alarm.setMonitorValue2(args[1]);// 告警位置长度
                    alarm.setMonitorValue3(args[2]);// 经纬度
                } else {
                    alarm.setAlarmMsg(alarmMsg);
                }
                final boolean b = alarm.insert();
                logger.info("告警插入结果：" + b);
                resultType = SaveUpdateEnum.ADD;
                if (sendEmailSms) {
                    // 设置缓存
                    if (deviceAlarmType.getAlarmTypeCode().equals(Constant.ALARM_Distance)) {
                        ConcurrentHashMapCacheUtils.setAlarmEmailCache(dbDevice.getDeviceSn(), channel, true);
                        snmpAlarmProcess.sendLengthAlarmEmail(dbDevice.getDeviceSn(), channel, alarmMsg, now);
                    }
                }
                snmpAlarmProcess.sendAlarmToWebsocketClient(deviceAlarmType, alarm, alarm.getAlarmLevel());
            }
            logger.info("resultType:" + resultType);
        } catch (Exception ex) {
            logger.info(ex.toString());
        }
        return resultType;
    }

    /**
     * 插入或更新告警
     *
     * @param alarmType  报警代码
     * @param dbDevice   设备
     * @param channel    通道
     * @param alarmLevel 部分告警有等级区分
     */
    SaveUpdateEnum saveOrUpdateAlarm(String alarmType, DeviceDevice dbDevice, Integer channel, String alarmLevel) {
        return saveOrUpdateAlarm(alarmType, dbDevice, channel, alarmLevel, "", false);
    }

    /**
     * 根据报警代码插入报警数据
     *
     * @param alarmType 报警代码
     * @param dbDevice  设备
     */
    SaveUpdateEnum saveOrUpdateAlarm(String alarmType, DeviceDevice dbDevice) {
        return saveOrUpdateAlarm(alarmType, dbDevice, 0, "");
    }

    /**
     * dts 温度告警判断
     *
     * @param deviceId  设备Id
     * @param channel   通道
     * @param tempCurve 温度曲线
     */
    @Async
    public void dtsAlarm(int deviceId, int channel, List<Double> tempCurve) {
        try {
            final DeviceMachineDtsConfig dtsConfig = dtsConfigService.getOneByDeviceIdAndChannel(deviceId, channel);
            if (dtsConfig == null) {
                logger.info("dts通道未配置告警阈值，不判断");
                return;
            }
            // 连续n个点的温度超过阈值就告警
            int persistAlarmCount = 0;
            int alarmStartPoint = 0;// 告警起点
            // 数据库以米为单位，这里曲线是1个点0.5米，所以要X2
            int startRange = dtsConfig.getStartRange() * 2;
            int endRange = dtsConfig.getEndRange() * 2;
            for (int i = 0; i < tempCurve.size(); i++) {
                if (i < startRange || i > endRange) {
                    continue;
                }
                if (tempCurve.get(i) > dtsConfig.getTempHigh()) {
                    // 记录告警数量
                    persistAlarmCount++;
                    if (persistAlarmCount > dtsConfig.getPersistCount() && alarmStartPoint == 0) {
                        // 记录报警启点
                        alarmStartPoint = i;
                    }
                } else {
                    if (persistAlarmCount < dtsConfig.getPersistCount()) {
                        persistAlarmCount = 0;
                    }
                }
            }

            if (persistAlarmCount > dtsConfig.getPersistCount()) {
                DeviceDevice device = deviceService.getById(deviceId);
                // 产生告警
                // 需要显示到首页，绑定光缆，并计算长度
                final BigDecimal alarmLength = new BigDecimal(alarmStartPoint).divide(new BigDecimal(2000), 3,
                        RoundingMode.HALF_DOWN);// 一个点0.5米
                // 转化为公里
                DeviceFibreMatch dfm = deviceFibreMatchService.selectByDeviceSnAndChannel(device.getDeviceSn(),
                        channel);
                final FibreFibre fibre = fibreService.getById(dfm.getFibreId());
                String location = snmpAlarmProcess.calcLocation(dfm, alarmLength);
                // DTS温度过高告警。光缆长度:60km,告警起点:25km,经纬度位置:114.438398762721,30.5125109605291
                // String msg = "DTS温度过高告警。光缆长度:" + fibre.getFibreLength() + ",告警起点:" +
                // alarmLength + ",经纬度位置:" + location;
                String msg = fibre.getFibreLength() + "|" + alarmLength + "|" + location;
                saveOrUpdateAlarm("TEMP_TOO_HIGH", device, channel, "", msg, false);
            }
        } catch (Exception ex) {
            logger.error("DTS告警判断异常：" + ex);
        }

    }
}
